package team.nmsg.ge.system.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;



import org.apache.shiro.authc.IncorrectCredentialsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;










import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import team.nmsg.ge.system.bean.AppToken;
import team.nmsg.ge.system.bean.RBACConstants;
import team.nmsg.ge.system.bean.SysOper;
import team.nmsg.ge.system.bean.SysRole;
import team.nmsg.ge.system.bean.SysUser;
import team.nmsg.ge.system.dao.ISysUserRepository;
import team.nmsg.ge.system.exception.NMSIllegalOperationException;
import team.nmsg.ge.system.exception.NMSTokenCantRefreshException;
import team.nmsg.ge.system.exception.NMSTokenExpireException;
import team.nmsg.ge.system.service.IAuthenticationService;
import team.nmsg.ge.system.service.ICacheService;
import team.nmsg.ge.system.util.MD5Encrypt;

/**
 * 
 * @author Guodeqi
 *
 */
@Transactional
@Service
public class AuthenticationServiceImpl implements IAuthenticationService {
	
	private static final Logger logger = LoggerFactory.getLogger(AuthenticationServiceImpl.class);
	
	@Autowired
	private ICacheService cacheService;
	
	@Autowired
	public ISysUserRepository sysUserRepository;	
	
	@Override
	@Transactional(propagation=Propagation.NOT_SUPPORTED)
	public Map<String, Object> userLogin(SysUser user){
		
		Map<String, Object> retValue = new HashMap<String, Object>();
		
		String username = user.getUsername();
		String password = user.getPassword();
		String md5Str = MD5Encrypt.getMD5Str(password);
		
		SysUser dbUser = sysUserRepository.findByUsernameAndPassword(username, md5Str);
		if( dbUser== null){
			throw new IncorrectCredentialsException("User name or password is invalid!");
		}
		if( dbUser.getUserStatus().intValue() == RBACConstants.USER_STATUS_ABNORMAL ){
			throw new IncorrectCredentialsException("Abnormal state of the user!");
		}
		
		AppToken createToken = createTokenByUser(dbUser ,  null);
		cacheService.addTokenToCahce(createToken);
		
		retValue.put("user", dbUser);
		retValue.put("token", createToken);
		
		return retValue;
	}
	
	private AppToken createTokenByUser( SysUser dbUser , String tokenStr ){
		Set<SysRole> roles = dbUser.getRoles();
		List<String> roleNames = new ArrayList<String>();
		Map<String, JSONObject> userOperMap = new HashMap<String, JSONObject>();
		List<String> operNames = new ArrayList<String>();
		for( SysRole r :  roles ){
			Set<SysOper> opers = r.getOpers();//加载具体权限 以便controller 中返回给APP,app中如果需要根据权限来判断按钮显示与否
			for( SysOper op : opers ){
				userOperMap.put(op.getOperName(), op.toJson());
			}
			roleNames.add(r.getRoleName() );
		}
		List<JSONObject> operJson = null;
		operJson = new ArrayList<JSONObject>(userOperMap.values());
		Collections.sort(operJson, new Comparator<JSONObject>() {
			@Override
			public int compare(JSONObject a, JSONObject b) {
				Integer valA = Integer.valueOf(a.getString("operId"));
				Integer valB = Integer.valueOf(b.getString("operId"));
				return valA.compareTo(valB);
			}
		});
		for(JSONObject oper:operJson){
			operNames.add(oper.toString());
		}
		AppToken newToken = AppToken.newToken( tokenStr );
		newToken.setUid(dbUser.getUserId());
		newToken.setRoles(roleNames);
		newToken.setOpers(operNames);
		newToken.setUserName(dbUser.getUsername());
		return newToken;
	}

	@Override
	@Transactional(propagation=Propagation.NOT_SUPPORTED)
	public boolean userLogout(AppToken token) {
		
		boolean result = cacheService.removeTokenFromCahce(token);
		
		return result;
	}

	@Override
	@Transactional(propagation=Propagation.NOT_SUPPORTED)
	public Map<String, Object> refreshToken(AppToken tokenObj) {
		
		Map<String, Object> retValue = new HashMap<String, Object>();
		
		AppToken cacheToken = cacheService.getTokenFromCache(tokenObj);
		if(  cacheToken==null ){
			throw new NMSTokenCantRefreshException();
		}
		
		long createTime = cacheToken.getCreateTime();
		long refreshTime = cacheToken.getRefreshTime();
		long expireTime = cacheToken.getExpireTime();
		long nowTime = System.currentTimeMillis();
		
		if(  (createTime +refreshTime*1000 ) < nowTime ){
			throw new NMSTokenCantRefreshException();
		}
		
		Long uid = cacheToken.getUid();
		SysUser dbUser = sysUserRepository.getOne( uid.intValue() );
		AppToken newToken = createTokenByUser(dbUser , tokenObj.getToken() );
		
		cacheService.updTokenFromCahce(newToken);
		
		retValue.put("user", dbUser);
		retValue.put("token", newToken);
		
		return retValue;
	}
	
	@Override
	@Transactional(propagation=Propagation.NOT_SUPPORTED)
	public AppToken checkClientTokenStr( String tokenStr ) throws NMSIllegalOperationException , NMSTokenExpireException , NMSTokenCantRefreshException {
		
		AppToken fromClientToken = AppToken.fromClientToken(tokenStr);
		
		AppToken tokenFromCache = cacheService.getTokenFromCache(fromClientToken);
		if( tokenFromCache==null ){
			throw new NMSIllegalOperationException("invalid token");
		}
		
		long tmpCreateTime = tokenFromCache.getCreateTime();
		long tmpExpireTime = tokenFromCache.getExpireTime();
		long tmpResfreshTime = tokenFromCache.getRefreshTime();
		
		long currentTime = System.currentTimeMillis();
		if( (tmpCreateTime +tmpResfreshTime*1000)< currentTime ){
			throw new NMSTokenCantRefreshException();
		}
		if( (tmpCreateTime +tmpExpireTime*1000)< currentTime ){
			throw new NMSTokenExpireException("expire token");
		}
		
		return tokenFromCache;
	}
	
	
	
}
